﻿using System;
using System.Collections.Generic;
using log4net;
using Needle.Bus.Messages;
using Needle.Bus.Messages.Management;

namespace Needle.Bus.Engine.MessageHandlers
{
    public class StageController : IMessageHandler
    {

        private static ILog _log = LogManager.GetLogger(typeof(StageController));

        private Dictionary<Type, Action<IMessage>> handlers;

        public StageController()
        {
            handlers = new Dictionary<Type, Action<IMessage>>();

            AddHandler<StartStageMessage>(StartStage);
            AddHandler<SetBufferCapacityMessage>(SetBufferCapacity);
            AddHandler<EnablePerfCounterMessage>(EnablePerfCounter);
        }
  
        public void Initialize()
        {

        }

        public void Process(IList<IMessage> messages)
        {
            foreach (var msg in messages)
                Process(msg);
        }

        public void Process(IMessage message)
        {
            if (message == null)
                return;
            Action<IMessage> handler = null;
            if (handlers.TryGetValue(message.GetType(), out handler))
                handler(message);
            else
                Bus.Send(BusAddress.RejectedMessages, message);
        }

        public void Dispose()
        {

        }

        private void AddHandler<TMessage>(Action<TMessage> handler) where TMessage : IMessage
        {
            handlers.Add(typeof(TMessage), m => handler((TMessage)m));
        }

        #region handlers

        private void SetBufferCapacity(SetBufferCapacityMessage msg)
        {
            var bus = Bus.Context.Engine;
            IStage stage = bus.GetStage(msg.StageId);
            if (stage == null)
                Bus.Send(BusAddress.RejectedMessages, msg);
            else
            {
                int oldCapacity = stage.Buffer.Capacity;
                stage.Buffer.Capacity = msg.BufferCapacity;
                _log.DebugFormat("Changed buffer capacity of stage {0} on bus {1} from {2} to {3}", msg.StageId, bus.Id, oldCapacity, msg.BufferCapacity);
            }
        }

        private void EnablePerfCounter(EnablePerfCounterMessage msg)
        {
            var bus = Bus.Context.Engine;
            IStage stage = bus.GetStage(msg.StageId);
            if (stage == null)
                Bus.Send(BusAddress.RejectedMessages, msg);
            else
            {
                stage.PerfCounter.IsEnabled = msg.Enable;
                _log.DebugFormat("{0} performance counter of stage {1} on bus {2}", msg.Enable ? "Enabled" : "Disabled", msg.StageId, bus.Id);
            }
        }

        private void StartStage(StartStageMessage msg)
        {
            var bus = Bus.Context.Engine;
            try
            {
                if (msg.Handler == null)
                    bus.StartStage(msg.StageId);
                else
                    bus.StartStage(msg.StageId, msg.Handler);

                _log.DebugFormat("Started stage {0} on bus {1}", msg.StageId, bus.Id);

                try
                {
                    if (!String.IsNullOrEmpty(msg.ReturnAddress))
                        Bus.Send(msg.ReturnAddress, SuccesMessage.Create(msg));
                }
                catch (Exception ex)
                {
                    _log.Error(string.Format("Failed to send succes message for starting stage {0} to stage {1} on bus {2}", msg.StageId, msg.ReturnAddress, bus.Id), ex);
                }

            }
            catch (Exception ex)
            {
                _log.Error(string.Format("Failed to start stage {0} on bus {1}", msg.StageId, bus.Id), ex);
            }
        }

        #endregion

    }
}
